#include "MysqlDao.h"
#include "ConfigMgr.h"

MysqlDao::MysqlDao()
{
    auto & cfg = ConfigMgr::GetInstance();
    std::string host = cfg["Mysql"]["Host"];
    std::string port = cfg["Mysql"]["Port"];
    std::string user = cfg["Mysql"]["User"];
    std::string passwd = cfg["Mysql"]["Passwd"];
    std::string schema = cfg["Mysql"]["Schema"];
    pool_ = std::make_unique<MysqlPool>(5,host+":"+port,user,passwd,schema);
}
MysqlDao::~MysqlDao()
{
    pool_.reset();
}
int MysqlDao::RegUser(const std::string&name, const std::string& email, const std::string& pwd)
{
    auto conn = pool_->GetConnection();

    try
    {
        if(!conn)
        {
            std::cerr<<"MysqlDao::RegUser get connection failed"<<std::endl;
            return -1;
        }
        //利用RAII机制，确保连接在函数退出时被释放
        Defer defer([this,&conn](){
            pool_->ReturnConnection(std::move(conn));
        });

        // 准备调用存储过程
        std::unique_ptr < sql::PreparedStatement > stmt(conn->con_->prepareStatement("CALL reg_user(?,?,?,@result)"));
        // 设置参数
        stmt->setString(1, name);
        stmt->setString(2, email);
        stmt->setString(3, pwd);


        // 执行存储过程
        stmt->execute();

        std::unique_ptr<sql::Statement> stmtResult(conn->con_->createStatement());
        std::unique_ptr<sql::ResultSet> res(stmtResult->executeQuery("SELECT @result AS result"));
        if(res->next())
        {
            //这个结果其实是uid
            int result = res->getInt("result");
            std::cout<<"MysqlDao::RegUser result:"<<result<<std::endl;
            return result;
        }
        return -1;
    }
    catch(const sql::SQLException& e)
    {
        std::cerr << e.what() << '\n';
        return -1;
    }
    
}

bool MysqlDao::CheckEmail(const std::string& name, const std::string& email)
{
    auto conn = pool_->GetConnection();
    if(!conn)
    {
        std::cerr<<"MysqlDao::CheckEmail get connection failed"<<std::endl;
        return false;
    }
    //使用RAII机制，确保连接在退出时被释放
    Defer defer([this,&conn](){
        pool_->ReturnConnection(std::move(conn));
    });
    try
    {
        // 准备SQL语句，并使用智能指针管理
        std::unique_ptr<sql::PreparedStatement> stmt(conn->con_->prepareStatement("select email from user where name = ?"));
        stmt->setString(1,name);
        //执行查询，并使用智能指针管理结果集
        std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
        while(res->next())
        {
            std::cout<<"Check Email: "<<res->getString("email")<<std::endl;
            std::string db_email = res->getString("email");
            if(db_email==email)
            {
                return true;
            }
        }
        return false;


    }catch(sql::SQLException& e)
    {
        std::cerr << "MysqlDao::CheckEmail error:" << e.what() << '\n';
        return false;
    }
}

bool MysqlDao::UpdatePassword(const std::string& name, const std::string& pwd)
{
    auto conn = pool_->GetConnection();
    if(!conn)
    {
        std::cout<<"MysqlDao::UpdatePassword get connection failed"<<std::endl;
        return false;
    }
    Defer defer([this,&conn](){
        pool_->ReturnConnection(std::move(conn));
    });

    try
    {
        std::unique_ptr<sql::PreparedStatement> stmt(conn->con_->prepareStatement("update user set pwd = ? where name = ?"));
        stmt->setString(1,pwd);
        stmt->setString(2,name);
        int rows = stmt->executeUpdate();
        if(rows==0)
        {
            std::cout<<"MysqlDao::UpdatePassword no rows affected\n";
            return false;
        }
        return true;
    }
    catch(sql::SQLException& exception)
    {
        std::cerr << "MysqlDao::UpdatePassword error:" << exception.what() << '\n';
        return false;
    }
    
}


bool MysqlDao::CheckPwd(const std::string& name, const std::string& pwd,UserInfo& userinfo)
{
    std::unique_ptr<SqlConnection> conn = pool_->GetConnection();
    if(!conn)
    {
        std::cout<<"MysqlDao::CheckPwd get connection failed"<<std::endl;
        return false;
    }
    Defer defer([this,&conn](){
        pool_->ReturnConnection(std::move(conn));
    });
    try
    {

        std::unique_ptr<sql::PreparedStatement> stmt(conn->con_->prepareStatement("select * from user where name = ?"));
        stmt->setString(1,name);
        
        std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
        std::string origin_pwd = "";
        while(res->next())
        {
            origin_pwd = res->getString("pwd");
            
            break;
        }
        if(origin_pwd==pwd)
        {
            userinfo.uid = res->getInt("uid");
            userinfo.name = res->getString("name");
            userinfo.email = res->getString("email");
            return true;
        }

        return false;
    }
    catch(const sql::SQLException &exception)
    {
        std::cerr<<"MysqlDao::CheckPwd error:" << exception.what() << '\n';
        return false;
    }
    

}

std::shared_ptr<UserInfo> MysqlDao::GetUser(int uid)
{
        std::unique_ptr<SqlConnection> conn = pool_->GetConnection();
        if(!conn)
        {
            std::cout<<"MysqlDao::GetUser get connection failed"<<std::endl;
            return nullptr;
        }
        Defer defer([this,&conn](){
            pool_->ReturnConnection(std::move(conn));
        });
        try
        {
            std::unique_ptr<sql::PreparedStatement> stmt(conn->con_->prepareStatement("select * from user where uid = ?"));
            stmt->setInt(1,uid);
            std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
            if(res->next())
            {
                std::shared_ptr<UserInfo> user_info = std::make_shared<UserInfo>();
                user_info->uid = res->getInt("uid");
                user_info->name = res->getString("name");
                user_info->email = res->getString("email");
                user_info->pwd = res->getString("pwd");

                return user_info;
            }
            return nullptr;
        }
        catch(const sql::SQLException &exception)
        {
            std::cerr<<"MysqlDao::GetUser error:" << exception.what() << '\n';
            return nullptr;
        }

}




std::shared_ptr<UserInfo> MysqlDao::GetUserByName(const std::string& name)
{
    std::unique_ptr<SqlConnection> conn = pool_->GetConnection();
    if(!conn)
    {
        std::cout<<"MysqlDao::GetUserByName get connection failed"<<std::endl;
        return nullptr;
    }
    Defer defer([this,&conn](){
        pool_->ReturnConnection(std::move(conn));
    });
    try
    {
        std::unique_ptr<sql::PreparedStatement> stmt(conn->con_->prepareStatement("select * from user where name = ?"));
        stmt->setString(1,name);
        std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
        if(res->next())
        {
            std::shared_ptr<UserInfo> user_info = std::make_shared<UserInfo>();
            user_info->uid = res->getInt("uid");
            user_info->name = res->getString("name");
            user_info->email = res->getString("email");
            user_info->pwd = res->getString("pwd");
            return user_info;
        }
        return nullptr;
    }
    catch(const sql::SQLException &exception)
    {
        std::cerr<<"MysqlDao::GetUserByName error:" << exception.what() << '\n';
        return nullptr;
    }
}

bool MysqlDao::AddFriendApply(int from_uid,int to_uid)
{
    std::unique_ptr<SqlConnection> conn = pool_->GetConnection();
    if(!conn)
    {
        std::cout<<"MysqlDao::AddFriendApply get connection failed"<<std::endl;
        return false;
    }

    // 使用RAII思想在函数结束时，将连接归还
    Defer Defer([this,&conn](){
        pool_->ReturnConnection(std::move(conn));
    });
    try
    {
        std::unique_ptr<sql::PreparedStatement> stmt(conn->con_->prepareStatement("insert into friend_apply (from_uid,to_uid) values(?,?)"
            "ON DUPLICATE KEY UPDATE from_uid = from_uid,to_uid = to_uid"));
        stmt->setInt(1,from_uid);
        stmt->setInt(2,to_uid);
        int rowAffected = stmt->executeUpdate();
        return rowAffected > 0;
    }
    catch(const sql::SQLException &e)
    {
        std::cout<<"MysqlDao::AddFriendApply error"<<std::endl;
        std::cerr << e.what() << '\n';
        return false;
    }
    
}

bool MysqlDao::GetApplyFriendList(int to_uid,std::vector<std::shared_ptr<ApplyInfo>>& apply_list,int offset,int limit)
{
    // 获取连接
    std::unique_ptr<SqlConnection> conn = pool_->GetConnection();
    if(!conn)
    {
        std::cout<<"MysqlDao::GetApplyFriendList get connection failed"<<std::endl;
        return false;
    }
    Defer defer([this,&conn](){
        pool_->ReturnConnection(std::move(conn));
    });
    try
    {
        std::unique_ptr<sql::PreparedStatement> stmt(conn->con_->prepareStatement("select apply.from_uid,apply.status,user.name, "
            "user.nick,user.sex from friend_apply as apply join user on apply.from_uid = user.uid where apply.to_uid = ? "
            "and apply.id > ? order by apply.id asc limit ?"));
        stmt->setInt(1,to_uid);
        stmt->setInt(2,offset);
        stmt->setInt(3,limit);
        std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
        while(res->next())
        {

            /*
            
            	:_uid(uid)
                ,_name(name)
                ,_desc(desc)
                ,_icon(icon)
                ,_nick(nick)
                ,_sex(sex)
                ,_status(status)
            */
           int from_uid = res->getInt("from_uid");
           std::string name = res->getString("name");
           std::string desc = "";
           std::string icon = "";
           std::string nick = res->getString("nick");
           int sex = res->getInt("sex");
           int status = res->getInt("status");
            
            std::shared_ptr<ApplyInfo> apply_info = 
                    std::make_shared<ApplyInfo>(from_uid,name,desc,icon,nick,sex,status);

            apply_list.push_back(apply_info);
        }
        return true;
    }
    catch(const sql::SQLException &e)
    {
        std::cout<<"MysqlDao::GetApplyFriendList error"<<std::endl;
        std::cerr << e.what() << '\n';
        return false;
    }
    
}
